Виджеты. Stateless и Stateful
➡️Скачать презентацию. Flutter Stateful
➡️Ссылка на репозиторий с кодом этого урока
Подготовка
- Создаём новую
git веткус названием4.1-Stateless-Stateful - В директории
libсоздадим новую папкуstateful_widgets - Добавим туда новый файл с названием
s1_stateful_widget.dart
Stateless Виджет
Сделаем виджет музыкального трека, где можно поставить Like.
После нажатия на иконку сердечка, музыкальный трек добавляется в избранное, а иконка закрашивается в красный цвет. Сделаем верстку и добавим кнопку-иконку

Файл s1_stateful_widget.dart
import 'package:flutter/material.dart';
class StateExample extends StatelessWidget {
const StateExample({super.key});
@override
Widget build(BuildContext context) {
return Card(
child: ListTile(
leading: Image.asset("assets/images/gu.webp"),
title: Text("Stateless"),
subtitle: Text("Flutter Vibes".toUpperCase()),
trailing: IconButton(
onPressed: () {},
icon: Icon(Icons.favorite_border),
),
),
);
}
}
Файл main.dart
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: "Flutter Course",
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
),
home: Scaffold(
body: Container(
padding: EdgeInsets.symmetric(horizontal: 32),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Color(0xFFBFF098), Color(0xFF6FD6FF)],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
),
child: Center(
child: StateExample(),
),
),
),
);
}
}
Добавим логику обработки нажатия на кнопку
Нужна булева переменная, которая будет хранить состояние кнопки лайка (нажата или не нажата).
Если кнопка нажата, то красим её в красный цвет, иначе убираем цвет.
Файл s1_stateful_widget.dart
import 'package:flutter/material.dart';
class StateExample extends StatelessWidget {
const StateExample({super.key});
@override
Widget build(BuildContext context) {
// Нажали на лайк или нет
bool isLiked = false;
return Card(
child: ListTile(
leading: Image.asset("assets/images/gu.webp"),
title: Text("Stateless"),
subtitle: Text("Flutter Vibes".toUpperCase()),
trailing: IconButton(
onPressed: () {
// Меняем значение на противоположное
isLiked = !isLiked;
},
// Если лайк поставлен то красный цвет, иначе стандартный
icon: isLiked
? Icon( Icons.favorite, color: Colors.red)
: Icon(Icons.favorite_border),
),
),
);
}
}

Нажимаем кнопку и ... ничего не происходит! 😢Почему?
Дело в том что StateExample это StatelessWidget, виджет без сохранения состояния!
Т.е. при изменении состояния кнопки (нажата/не нажата) ничего не происходит, потому что виджет не умеет работать с состоянием и реагировать на его изменение.
Нужно использовать второй тип виджетов во Flutter это Stateful виджеты!
Такие виджеты отслеживают изменения состояния и запускают перерисовку виджета, чтобы обновить интерфейс.
Stateful Виджет
Чтобы переделать StatelessWidget в StatefulWidget нужно написать два новых класса
- Класс наследник
StatefulWidget - Класс наследник
State
Используем сниппеты в VSCode для создания StatefulWidget или
- Нажимаем правой кнопкой мыши на
StatelessWidget - Выбираем
Refactor… - Выбираем
Convert to StatefulWidget
Visual Studio Code

Используем сниппеты в Android Studio для StatefulWidget или
- Нажимаем
alt + EnterнаStatelessWidget - Выбираем
Convert to StatefulWidget
Android Studio

Как правильно обновлять состояние виджета
- Использовать
StatefulWidget - Вызвать специальный метод
setState() - Вызов этого метода, заставляет автоматически запустить метод
build() - Метод
build()перестраивает/перерисовывает виджет на экране
Рефакторинг stateless виджета
Состояние isLiked нужно перенести за пределы метода build() !!!
Потому что, если оставить его в методе build(), то при каждом вызове build() в переменной isLiked будет одно и тоже значение false, что не имеет смысла.
🔥После изменения состояния нужно обязательно вызывать метод setState()
Файл s1_stateful_widget.dart
import 'package:flutter/material.dart';
class StateExample extends StatefulWidget {
const StateExample({super.key});
@override
State<StateExample> createState() => _StateExampleState();
}
class _StateExampleState extends State<StateExample> {
bool isLiked = false; // За предлами метода build
@override
Widget build(BuildContext context) {
return Card(
child: ListTile(
leading: Image.asset("assets/images/gu.webp"),
title: Text("Stateless"),
subtitle: Text("Flutter Vibes".toUpperCase()),
trailing: IconButton(
onPressed: () {
// Вызываем setState() для обновления интерфейса
setState(() {
isLiked = !isLiked;
});
},
icon: isLiked
? Icon(Icons.favorite, color: Colors.red)
: Icon(Icons.favorite_border),
),
),
);
}
}

setState()
Cообщает фреймворку, что состояние изменилось и нужно перерисовать UI.
Технически заставляет виджет вызвать снова метод build()
StatefulWidget
Это тип виджета во Flutter, который позволяет сохранять и обновлять состояние во время жизненного цикла приложения. Это означает, что виджет может изменять свое поведение и внешний вид в зависимости от изменения в его состоянии.
.png)
UI = f(state)
UI : верстка на экране
f : методы build()
state : состояние приложения
-
Statelesswidgets (без сохранения/управления состоянием) - они имутабельны (НЕ ИЗМЕНЯЕМЫЕ). Имутабельные объекты - их свойства нельзя изменить, все значенияfinal -
Statefulwidgets (с сохранением/управлением состояния) наоборотИЗМЕНЯЕМЫЕ, они содержатstate(состояние приложения), которое со временем может изменяться!
Изолированные Stateful виджеты
Flutter очень умный фреймворк.
- Если мы создадим несколько виджетов
StateExample, то у каждого будет своё состояние. - Перерисовыватья будет только тот виджет, где изменяется состояние.

Проверим сколько раз будет срабатывать метод build()
Временно добавим в него функцию debugPrint()
@override
Widget build(BuildContext context) {
debugPrint("Update UI");
return Card(...);
}
При первом построении экрана "Update UI" вызвалось 4 раза, потому что 4 карточки.
При изменении состояния у одной из карточек, в консоль пишется только 1 "Update UI".
Значит обновляется только конкретный виджет, а не весь экран.